﻿// -----------------------------------------------------------------------
// <copyright file="PushStatsService.cs" company="Zen Design Corp">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Zen.BlackBerry.Push.Stats
{
	using System;
	using System.Threading.Tasks;
	using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
	using Microsoft.Practices.Unity;
	using Zen.BlackBerry.Configuration;
	using Zen.BlackBerry.Push.AppManagement;

	/// <summary>
	/// Implementation of the <see cref="IPushStatsService"/> interface.
	/// </summary>
	public class PushStatsService : IPushStatsService
	{
		private static readonly string ExceptionPolicyName = "BPSS Statistics";

		private IPushProperties _pushProperties;
		private IPushStatsRepository _statsRepository;
		private IPushApplicationService _appService;
		private IPushStatsBatchUpdaterService _batchUpdaterService;
		private ExceptionManager _exceptionManager;
		private DateTime? _lastBatchUpdate;

		/// <summary>
		/// Initializes a new instance of the <see cref="PushStatsService"/> class.
		/// </summary>
		/// <param name="pushProperties">The push properties.</param>
		/// <param name="statRepository">The stat repository.</param>
		/// <param name="appService">The app service.</param>
		/// <param name="batchUpdaterService">The batch updater service.</param>
		/// <param name="exceptionManager">The exception manager.</param>
		public PushStatsService(
			[Dependency]IPushProperties pushProperties,
			[Dependency]IPushStatsRepository statRepository,
			[Dependency]IPushApplicationService appService,
			[Dependency]IPushStatsBatchUpdaterService batchUpdaterService,
			[Dependency]ExceptionManager exceptionManager)
		{
			_pushProperties = pushProperties;
			_statsRepository = statRepository;
			_appService = appService;
			_batchUpdaterService = batchUpdaterService;
			_exceptionManager = exceptionManager;

			PurgeUpdates();
		}

		private async void PurgeUpdates()
		{
			while (true)
			{
				// Determine whether we need to purge updates
				bool doUpdates = false;
				if (_batchUpdaterService.PushStatsQueueSize >= _pushProperties.PushStatsUpdateQueueSize)
				{
					doUpdates = true;
				}

				DateTime utcNow = DateTime.UtcNow;
				if (_lastBatchUpdate.HasValue &&
					(utcNow - _lastBatchUpdate) > _pushProperties.PushStatsUpdateFrequency)
				{
					doUpdates = true;
				}

				if (doUpdates)
				{
					_lastBatchUpdate = utcNow;
					_batchUpdaterService.Run();
				}

				await Task.Delay(TimeSpan.FromMinutes(1));
			}
		}

		#region IPushStatsService Members

		/// <summary>
		/// Gets the remaining quota (in number of pushes) for the specified
		/// push application.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		/// <param name="preferUseCache">if set to <c>true</c> [prefer use cache].</param>
		/// <returns></returns>
		public long GetRemainingQuota(string applicationId, bool preferUseCache)
		{
			return _exceptionManager.Process(
				() =>
				{
					long remainingQuota = 0;
					PushApplication app = _appService.FindByAppId(applicationId);
					PushStats stats = _statsRepository.GetPushStats(applicationId, preferUseCache);
					if (stats != null)
					{
						remainingQuota = app.MaximumDailyPushes - stats.DailyPushCount;
					}
					else if (app != null)
					{
						remainingQuota = app.MaximumDailyPushes;
					}
					return remainingQuota;
				},
				ExceptionPolicyName);
		}

		/// <summary>
		/// Gets the total bytes of content pushed to the specified push
		/// application.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		/// <returns></returns>
		public long GetRunningContentSum(string applicationId)
		{
			return _exceptionManager.Process(
				() =>
				{
					long result = 0;
					PushStats stats = _statsRepository.GetPushStats(applicationId, true);
					if (stats != null)
					{
						result = stats.RunningContentSum;
					}
					return result;
				},
				ExceptionPolicyName);
		}

		/// <summary>
		/// Gets a running count of pushes for the specified push application.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		/// <returns></returns>
		public long GetRunningPushCount(string applicationId)
		{
			return _exceptionManager.Process(
				() =>
				{
					long result = 0;
					PushStats stats = _statsRepository.GetPushStats(applicationId, true);
					if (stats != null)
					{
						result = stats.RunningPushCount;
					}
					return result;
				},
				ExceptionPolicyName);
		}

		/// <summary>
		/// Increments the push statistics for a push application by the given
		/// push count and total content sum.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		/// <param name="pushCount">The push count.</param>
		/// <param name="contentSum">The content sum.</param>
		public void IncrementPushStats(string applicationId, int pushCount, long contentSum)
		{
			_exceptionManager.Process(
				() =>
				{
					PushStats stats = new PushStats
						{
							ApplicationId = applicationId,
							DailyPushCount = pushCount,
							DailyPushCountDate = DateTime.UtcNow,
							RunningContentSum = contentSum,
							RunningPushCount = pushCount
						};
					_batchUpdaterService.AddPushStatsUpdate(stats);
					_lastBatchUpdate = DateTime.UtcNow;
				},
				ExceptionPolicyName);
		}

		/// <summary>
		/// Resets the running content sum pushed to the specified push
		/// application.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		public void ResetRunningContentSum(string applicationId)
		{
			_batchUpdaterService.ResetRunningContentSum(applicationId);
			_exceptionManager.Process(
				() =>
				{
					_statsRepository.ResetRunningContentSum(applicationId);
				},
				ExceptionPolicyName);
		}

		/// <summary>
		/// Resets the running push count for the specified push application.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		public void ResetRunningPushCount(string applicationId)
		{
			_batchUpdaterService.ResetRunningPushCount(applicationId);
			_exceptionManager.Process(
				() =>
				{
					_statsRepository.ResetRunningPushCount(applicationId);
				},
				ExceptionPolicyName);
		}

		#endregion
	}
}
